<?php
/**
 * @copyright ©2018 Lu Wei
 * @author Lu Wei
 * @link http://www.luweiss.com/
 *
 * Created by IntelliJ IDEA.
 * Date Time: 2018/4/14 12:55
 */


namespace luweiss\wechat;

/**
 * @property Wechat $wechat
 */
class Pay
{
    /** @var  Wechat $wechat */
    private $wechat;

    public function __construct($wechat)
    {
        $this->wechat = $wechat;
    }

    /**
     * 微信支付统一下单接口，详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
     * @param array $args
     * [
     *
     * //必填参数
     *
     * 'body'=>'商品描述(128)',
     *
     * 'out_trade_no'=>'商户订单号(32)',
     *
     * 'total_fee'=>'标价金额(分)',
     *
     * 'notify_url'=>'通知地址URL，不能带参数(256)',
     *
     * 'trade_type'=>'JSAPI|NATIVE|APP',
     *
     * 'openid'=>'trade_type=JSAPI时必填',
     *
     * 'product_id'=>'商品ID，trade_type=NATIVE时必填',
     *
     * //选填参数
     *
     * 'device_info'=>'设备号(32)',
     *
     * 'detail'=>'商品详情(6000)',
     *
     * 'attach'=>'附加数据(127)',
     *
     * 'limit_pay'=>'指定支付方式(127)',
     *
     * 'spbill_create_ip'=>'终端IP(16)',
     *
     * ]
     * @return bool|array 失败时返回false，成功时返回以下数据
     * [
     * 'return_code'=>'',
     * 'return_msg'=>'',
     * 'appid'=>'',
     * 'mch_id'=>'',
     * 'nonce_str'=>'',
     * 'sign'=>'',
     * 'result_code'=>'',
     * 'prepay_id'=>'',
     * 'trade_type'=>'',
     * 'code_url'=>'trade_type为NATIVE时有返回，例：weixin://wxpay/s/An4baqw',
     * ]
     */
    public function unifiedOrder($args)
    {
        if (empty($args)) {
            $this->wechat->errMsg = '提交参数不能为空';
            $this->wechat->errFun = __FUNCTION__;
            return false;
        }
        $data = [
            'appid' => $this->wechat->appId,
            'mch_id' => $this->wechat->mchId,
            'nonce_str' => $this->wechat->randomString(32),
            'sign_type' => 'MD5',
            'body' => $args['body'],
            'out_trade_no' => $args['out_trade_no'],
            'total_fee' => $args['total_fee'],
            'spbill_create_ip' => isset($args['spbill_create_ip']) ? $args['spbill_create_ip'] : '127.0.0.1',
            'notify_url' => $args['notify_url'],
            'trade_type' => $args['trade_type'],
        ];
        if ($data['trade_type'] == 'JSAPI') {
            $data['openid'] = $args['openid'];
        }
        if ($data['trade_type'] == 'NATIVE') {
            $data['product_id'] = $args['product_id'];
        }
        if (isset($args['device_info']))
            $data['device_info'] = $args['device_info'];
        if (isset($args['detail']))
            $data['detail'] = $args['detail'];
        if (isset($args['attach']))
            $data['attach'] = $args['attach'];
        if (isset($args['limit_pay']))
            $data['limit_pay'] = $args['limit_pay'];
        $data['sign'] = $this->makeSign($data);
        $xml = DataTransform::arrayToXml($data);
        $api = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        $res = $this->wechat->curl->post($api, $xml);
        if (!$res) {
            $this->wechat->errCode = $this->wechat->curl->curlErrNo;
            $this->wechat->errMsg = $this->wechat->curl->curlError;
            $this->wechat->errFun = __FUNCTION__;
            return false;
        }
        $res = DataTransform::xmlToArray($res);
        if (!$res) {
            $this->wechat->errMsg = 'XML数据转换失败。';
            $this->wechat->errFun = __FUNCTION__;
            return false;
        }
        if (isset($res['return_code']) && $res['return_code'] == 'FAIL') {
            $this->wechat->errCode = $res['return_code'];
            $this->wechat->errMsg = $res['return_msg'];
            $this->wechat->errFun = __FUNCTION__;
            return false;
        }
        if (isset($res['result_code']) && $res['result_code'] == 'FAIL') {
            $this->wechat->errCode = $res['result_code'];
            $this->wechat->errMsg = $res['err_code_des'];
            $this->wechat->errFun = __FUNCTION__;
            return false;
        }
        return $res;
    }

    /**
     * 微信支付申请退款接口，详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
     * @param array $args
     * [
     *
     * //必填参数
     *
     * 'transaction_id'=>'微信订单号(32),out_trade_no二选一',
     *
     * 'out_trade_no'=>'商户订单号(32),transaction_id二选一',
     *
     * 'out_refund_no'=>'商户退款单号(32)',
     *
     * 'total_fee'=>'订单总金额(分)',
     *
     * 'refund_fee'=>'退款金额(分)',
     *
     * //选填参数
     *
     * 'notify_url'=>'通知地址URL，不能带参数(256)',
     *
     * 'refund_desc'=>'退款原因(80)',
     *
     * ]
     * @return bool|array 失败时返回false，成功时返回以下数据
     * [
     * 'return_code'=>'',
     * 'return_msg'=>'',
     * 'result_code'=>'',
     * 'err_code'=>'',
     * 'err_code_des'=>'',
     * 'appid'=>'',
     * 'mch_id'=>'',
     * 'nonce_str'=>'',
     * 'sign'=>'',
     * 'transaction_id'=>'',
     * 'out_trade_no'=>'',
     * 'out_refund_no'=>'',
     * 'refund_id'=>'',
     * 'refund_fee'=>'',
     * 'settlement_refund_fee'=>'',
     * 'total_fee'=>'',
     * 'settlement_total_fee'=>'',
     * 'fee_type'=>'',
     * 'cash_fee'=>'',
     * 'cash_fee_type'=>'',
     * 'cash_refund_fee'=>'',
     * ''=>'',
     * ]
     */
    public function refund($args)
    {
        if (empty($args)) {
            $this->wechat->errMsg = '提交参数不能为空';
            $this->wechat->errFun = __FUNCTION__;
            return false;
        }
        if (!$this->wechat->certPem || !$this->wechat->keyPem || !file_exists($this->wechat->certPem) || !file_exists($this->wechat->keyPem)) {
            $this->wechat->errMsg = '支付证书不存在或尚未配置';
            $this->wechat->errFun = __FUNCTION__;
            return false;
        }
        $data = [
            'appid' => $this->wechat->appId,
            'mch_id' => $this->wechat->mchId,
            'nonce_str' => $this->wechat->randomString(32),
            'sign_type' => 'MD5',
            'out_refund_no' => $args['out_refund_no'],
            'total_fee' => $args['total_fee'],
            'refund_fee' => $args['refund_fee'],
        ];
        if (isset($args['transaction_id']))
            $data['transaction_id'] = $args['transaction_id'];
        if (isset($args['out_trade_no']))
            $data['out_trade_no'] = $args['out_trade_no'];
        if (isset($args['refund_desc']))
            $data['refund_desc'] = $args['refund_desc'];
        if (isset($args['notify_url']))
            $data['notify_url'] = $args['notify_url'];
        $data['sign'] = $this->makeSign($data);
        $xml = DataTransform::arrayToXml($data);
        $api = "https://api.mch.weixin.qq.com/secapi/pay/refund";
        $res = $this->wechat->curl
            ->setUseSsl(true)
            ->setSslCertPem($this->wechat->certPem)
            ->setSslKeyPem($this->wechat->keyPem)
            ->post($api, $xml);
        $this->wechat->curl->setUseSsl(false);
        if (!$res) {
            if ($this->wechat->curl->curlErrNo == 58) {
                $this->wechat->errCode = $this->wechat->curl->curlErrNo;
                $this->wechat->errMsg = '支付证书错误:' . $this->wechat->curl->curlError;
                $this->wechat->errFun = __FUNCTION__;
            } else {
                $this->wechat->errCode = $this->wechat->curl->curlErrNo;
                $this->wechat->errMsg = 'Curl Error:' . $this->wechat->curl->curlError;
                $this->wechat->errFun = __FUNCTION__;
            }
            return false;
        }
        $res = DataTransform::xmlToArray($res);
        if (!$res) {
            $this->wechat->errMsg = 'XML数据转换失败。';
            $this->wechat->errFun = __FUNCTION__;
            return false;
        }
        if (isset($res['return_code']) && $res['return_code'] == 'FAIL') {
            $this->wechat->errCode = $res['return_code'];
            $this->wechat->errMsg = $res['return_msg'];
            $this->wechat->errFun = __FUNCTION__;
            return false;
        }
        if (isset($res['result_code']) && $res['result_code'] == 'FAIL') {
            $this->wechat->errCode = $res['result_code'];
            $this->wechat->errMsg = $res['err_code_des'];
            $this->wechat->errFun = __FUNCTION__;
            return false;
        }
        return $res;
    }

    /**
     * 企业付款到用户零钱接口，详见https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2
     * @param array $args
     * [
     *
     * //必填参数
     *
     * 'partner_trade_no'=>'商户订单号(32)',
     *
     * 'openid'=>'用户openid',
     *
     * 'amount'=>'金额(分)',
     *
     * 'desc'=>'企业付款描述信息(128)',
     *
     * //选填参数
     *
     * 'check_name'=>'NO_CHECK|FORCE_CHECK',
     *
     * 're_user_name'=>'收款用户姓名,check_name=FORCE_CHECK时必填',
     *
     * 'device_info'=>'设备号(32)',
     *
     * ]
     * @return false|array
     */
    public function transfers($args)
    {
        if (empty($args)) {
            $this->wechat->errMsg = '提交参数不能为空';
            $this->wechat->errFun = __FUNCTION__;
            return false;
        }
        if (!$this->wechat->certPem || !$this->wechat->keyPem || !file_exists($this->wechat->certPem) || !file_exists($this->wechat->keyPem)) {
            $this->wechat->errMsg = '支付证书不存在或尚未配置';
            $this->wechat->errFun = __FUNCTION__;
            return false;
        }
        $data = [
            'mch_appid' => $this->wechat->appId,
            'mchid' => $this->wechat->mchId,
            'nonce_str' => $this->wechat->randomString(32),
            'partner_trade_no' => $args['partner_trade_no'],
            'openid' => $args['partner_trade_no'],
            'check_name' => isset($args['check_name']) ? $args['check_name'] : 'NO_CHECK',
            'amount' => $args['amount'],
            'desc' => $args['desc'],
            'spbill_create_ip' => isset($args['spbill_create_ip']) ? $args['spbill_create_ip'] : '127.0.0.1',
        ];
        if (isset($args['device_info']))
            $data['device_info'] = $args['device_info'];
        if (isset($args['re_user_name']))
            $data['re_user_name'] = $args['re_user_name'];
        $data['sign'] = $this->makeSign($data);
        $xml = DataTransform::arrayToXml($data);
        $api = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
        $res = $this->wechat->curl
            ->setUseSsl(true)
            ->setSslCertPem($this->wechat->certPem)
            ->setSslKeyPem($this->wechat->keyPem)
            ->post($api, $xml);
        $this->wechat->curl->setUseSsl(false);
        if (!$res) {
            if ($this->wechat->curl->curlErrNo == 58) {
                $this->wechat->errCode = $this->wechat->curl->curlErrNo;
                $this->wechat->errMsg = '支付证书错误:' . $this->wechat->curl->curlError;
                $this->wechat->errFun = __FUNCTION__;
            } else {
                $this->wechat->errCode = $this->wechat->curl->curlErrNo;
                $this->wechat->errMsg = 'Curl Error:' . $this->wechat->curl->curlError;
                $this->wechat->errFun = __FUNCTION__;
            }
            return false;
        }
        $res = DataTransform::xmlToArray($res);
        if (!$res) {
            $this->wechat->errMsg = 'XML数据转换失败。';
            $this->wechat->errFun = __FUNCTION__;
            return false;
        }
        if (isset($res['return_code']) && $res['return_code'] == 'FAIL') {
            $this->wechat->errCode = $res['return_code'];
            $this->wechat->errMsg = $res['return_msg'];
            $this->wechat->errFun = __FUNCTION__;
            return false;
        }
        if (isset($res['result_code']) && $res['result_code'] == 'FAIL') {
            $this->wechat->errCode = $res['result_code'];
            $this->wechat->errMsg = $res['err_code_des'];
            $this->wechat->errFun = __FUNCTION__;
            return false;
        }
        return $res;
    }


    /**
     * MD5签名
     * @param array $args
     * @return string
     */
    public function makeSign($args)
    {
        ksort($args);
        foreach ($args as $i => $arg) {
            if ($args === null || $arg === '')
                unset($args[$i]);
        }
        $string = DataTransform::arrayToUrlParam($args, false);
        $string = $string . "&key={$this->wechat->key}";
        $string = md5($string);
        $result = strtoupper($string);
        return $result;
    }


    /**
     * 验证MD5签名
     * @param array $args
     * @return bool|array 验证不通过或失败返回false，成功返回传入的参数
     */
    public function validateSign($args)
    {
        if (empty($args))
            return false;
        if (empty($args['sign']))
            return false;
        $sign = $args['sign'];
        if (!$sign)
            return false;
        $newArgs = $args;
        unset($newArgs['sign']);
        $newSign = $this->makeSign($newArgs);
        if ($newSign === $sign)
            return $args;
        return false;
    }
}